import * as React from 'react';
import { connect } from 'react-redux';
import * as classnames from 'classnames';
import { get, pullAt, reduce } from 'lodash';
import { Progress, Checkbox, Form, Button, Tooltip, Tag, Icon, Card, message } from 'antd';
import ContentEditable from 'components/common/ContentEditable';
import CustomModal from 'components/common/CustomModal';

import { recognitionAudio } from 'actions/lesson';
import LoadableComponent from 'components/common/LoadableComponent';
const LoadableKnowledgeList = LoadableComponent(() => import('./KnowledgeList'));
const LoadableEditWordTime = LoadableComponent(() => import('./EditWordTime'));
const LoadableDragger = LoadableComponent(() => import('components/common/Dragger'));
const LoadableMedia = LoadableComponent(() => import('components/common/Media'));
const LoadableHighlightSentence = LoadableComponent(() =>
  import('components/common/HighlightSentence')
);

import { isLoading } from 'utils/utils';
import ACTIONS from 'constants/lesson';

import './EditSentence.scss';
import edit from 'pages/knowledge/edit';

interface EditSentenceProps {
  dispatch: (action: any) => any;
  onOk: (data: any) => any;
  onCancel: () => any;
  form: any;
  editSentence: any;
  relateImages?: any[];
  needRelateImages?: boolean;
  needEditWords?: boolean;
  needAudio?: boolean;
  loadingGroup: any;
}

interface EditSentenceState {
  audio: any;
  words: any[];
  sentence: string;
  editKnowledgeWordIndex: number;
  relatedImageId: number;
  recognitionResult: any;
  isPlaying: boolean;
  playingTimeAt: number;
  audioDuration: number;
}
class EditSentence extends React.Component<EditSentenceProps, EditSentenceState> {
  audio: any;
  static defaultProps: EditSentenceProps = {
    dispatch: () => {},
    onOk: () => {},
    onCancel: () => {},
    form: null,
    editSentence: null,
    relateImages: [],
    needRelateImages: false,
    needEditWords: false,
    needAudio: true,
    loadingGroup: {},
  };
  constructor(props: EditSentenceProps) {
    super(props);

    const words = get(props.editSentence, 'words', []);
    const audio = get(props.editSentence, 'audio', null);
    const sentence = get(props.editSentence, 'sentence', '');
    const relatedImageId = get(props.editSentence, 'relatedImageId', 0);
    const audioDuration = this.getAudioDuration(words);

    this.state = {
      words,
      audio,
      sentence,
      relatedImageId,
      editKnowledgeWordIndex: -1,
      recognitionResult: null,
      isPlaying: false,
      playingTimeAt: -1,
      audioDuration,
    };
  }

  componentDidMount() {
    this.initPlay();
  }

  getAudioDuration = (words: any[]): number => {
    if (words.length === 0) return 1;
    const lastWord = words[words.length - 1];
    if (lastWord.end === undefined) {
      return 0;
    }
    const endTimesArr = String(lastWord.end).split(',');
    return Number(endTimesArr[endTimesArr.length - 1]);
  };

  // 初始化音频播放事件
  initPlay = () => {
    this.audio = new Audio();
    this.audio.addEventListener('ended', this.handlePlayEnded);
  };

  // 高亮预览播放结束
  handlePlayEnded = () => {
    this.setState({
      isPlaying: false,
      playingTimeAt: -1,
    });
  };

  // 高亮预览播放开始
  handlePlayBegin = () => {
    const { isPlaying, audio } = this.state;
    if (isPlaying) return;

    const audioUri = audio.uri;
    this.audio.src = audioUri;
    this.audio.play();

    this.handlePlayListen();
    this.setState({ isPlaying: true });
  };

  // 监听高亮预览音频播放时间更新
  handlePlayListen = () => {
    const { audioDuration } = this.state;

    const step = 0.05; // 秒
    let playingTimeAt = 0;
    const timer = setInterval(() => {
      playingTimeAt += step;
      if (playingTimeAt >= audioDuration) {
        clearInterval(timer);

        this.setState({
          playingTimeAt,
        });
        return;
      }

      this.setState({
        playingTimeAt: this.audio.currentTime,
      });
    }, step * 1000);
  };

  // 脚本编辑
  handleWithWordSubmit = (): any => {
    const { onOk, needAudio } = this.props;
    const { words, sentence, audio } = this.state;

    if (!sentence) {
      return message.error('请填写脚本');
    }
    let content: any = {
      sentence,
      words,
    };
    if (needAudio) {
      if (!audio) {
        return message.error('请选择音频');
      }
      content = { ...content, audio };
    }
    onOk(content);
  };

  // 精读关联图片
  handleWithImageSubmit = (): any => {
    const { onOk, needAudio } = this.props;
    const { relatedImageId, audio, sentence } = this.state;
    if (!sentence) {
      return message.error('请填写脚本');
    }
    if (!relatedImageId) {
      return message.error('请关联图片');
    }
    let content: any = {
      sentence,
      relatedImageId,
    };
    if (needAudio) {
      if (!audio) {
        return message.error('请选择音频');
      }
      content = { ...content, audio };
    }
    onOk(content);
  };

  handleEditKnowledge = (editKnowledgeWordIndex: number) => {
    this.setState({
      editKnowledgeWordIndex,
    });
  };

  // data为null则删除知识点关联
  handleKnowledgeCallback = (data: any = null) => {
    let { words, editKnowledgeWordIndex } = this.state;
    words = words.map((word: any, index: number) => {
      if (index === editKnowledgeWordIndex) {
        delete word.knowledgeId;
        delete word.assistId;
        delete word.type;
        if (data !== null) {
          word = { ...word, ...data };
        }
      }
      return word;
    });
    editKnowledgeWordIndex = -1;

    this.setState({
      words,
      editKnowledgeWordIndex,
    });
  };

  onSelectImageChange(image: any, e: any) {
    let relatedImageId = null;
    if (e.target.checked) {
      relatedImageId = image.id;
    }
    this.setState({
      relatedImageId,
    });
  }

  // 单个单词时间轴编辑提交
  // index: 单词在句子中的位置
  // innerIndex: 单词在单词组中的位置
  handleWordTimeEdit = (value: string, index: number, innerIndex: number, type: string) => {
    let { words } = this.state;
    if (words[index][type] !== undefined) {
      let oldValueStr = words[index][type];
      let oldValueArr = oldValueStr.split(',');
      oldValueArr[innerIndex] = value;
      words[index][type] = oldValueArr.join(',');
    } else {
      words[index][type] = value;
    }

    this.setState({
      words,
    });
  };

  // 根据语音识别结果，重置时间轴
  handleResetWordTimes = (times: any) => {
    let { words } = this.state;

    let now = 0;
    words = words.map((item, index) => {
      const itemArr = item.name.split(/\s+/);
      let groupStart = '';
      let groupEnd = '';
      for (let i = 0; i < itemArr.length; i++) {
        const [name, start, end] = times[index + now + i];
        groupStart += ',' + start;
        groupEnd += ',' + end;
      }
      now += itemArr.length - 1;
      groupStart = groupStart.substr(1);
      groupEnd = groupEnd.substr(1);

      return { ...item, start: groupStart, end: groupEnd };
    });

    const audioDuration = this.getAudioDuration(words);
    this.setState({
      words,
      audioDuration,
    });
  };

  // 音频识别时间轴
  handleAudioRecognition = (e: any) => {
    const { dispatch } = this.props;
    let { audio, sentence } = this.state;

    if (!audio || !sentence) {
      message.error('请选好音频和填写脚本先哦～');
      return;
    }

    // 识别之前先去除词组的-
    sentence = sentence.replace(/-/g, ' ');
    dispatch(recognitionAudio(audio.uri, sentence)).then((result: any) => {
      if (result.code) {
        message.error(result.msg);
      } else {
        const { timestamps, confidence, transcript } = result.response.data;
        this.setState({
          recognitionResult: {
            timestamps,
            confidence,
            transcript,
          },
        });
        this.handleResetWordTimes(timestamps);
      }
    });
  };

  renderFooterActions = () => {
    const { onCancel, needRelateImages, needAudio } = this.props;
    const { relatedImageId, sentence, audio } = this.state;

    let disabled = !sentence;
    if (needRelateImages) {
      disabled = disabled || !relatedImageId;
    }
    if (needAudio) {
      disabled = disabled || !audio;
    }

    return (
      <div style={{ margin: 24 }}>
        <Button
          key="back"
          type="ghost"
          size="large"
          onClick={() => {
            onCancel();
          }}>
          取消
        </Button>
        <Button
          key="submit"
          type="primary"
          size="large"
          loading={false}
          disabled={disabled}
          onClick={() => {
            if (needRelateImages) {
              this.handleWithImageSubmit();
            } else {
              this.handleWithWordSubmit();
            }
          }}>
          保存
        </Button>
      </div>
    );
  };

  // 不需要音频的则隐藏
  renderInput() {
    const { audio, sentence } = this.state;
    const { needEditWords, needAudio } = this.props;
    const contentTip = '请填写脚本' + (needEditWords ? '，以‘-’连接词组（如：I am a-boy）' : '');

    return (
      <Card
        bordered={false}
        title={
          <>
            <Icon type="edit" />
            &nbsp;编辑音频和脚本
          </>
        }>
        <Form layout="inline">
          {needAudio && (
            <Form.Item label="音频" style={{ verticalAlign: 'middle' }}>
              {audio ? (
                <div style={{ position: 'relative' }}>
                  <LoadableMedia
                    attachment={audio}
                    onDestroy={() => {
                      this.setState({
                        audio: null,
                      });
                    }}
                  />
                </div>
              ) : (
                <LoadableDragger
                  key="audio"
                  accept="audio"
                  selectMax={1}
                  onDone={(response: any) => {
                    this.setState({
                      audio: response.files[0],
                    });
                  }}>
                  <Button type="primary">
                    <Icon type="notification" />
                  </Button>
                </LoadableDragger>
              )}
            </Form.Item>
          )}
          <Form.Item label="脚本" style={{ verticalAlign: 'middle' }}>
            <ContentEditable
              multiline={true}
              tip={contentTip}
              onChange={(sentence: any) => {
                let words = [];
                sentence = sentence.trim();
                if (sentence) {
                  words = sentence.split(/\s+/).map((name: any) => {
                    name = name.split('-').join(' ');
                    return { name };
                  });
                }
                this.setState({
                  words,
                  sentence,
                });
              }}>
              {sentence}
            </ContentEditable>
          </Form.Item>
        </Form>
      </Card>
    );
  }

  renderImages() {
    const { relateImages } = this.props;
    const { relatedImageId } = this.state;

    return (
      <Card
        title={
          <>
            <Icon type="picture" />
            &nbsp;关联图片
          </>
        }
        bordered={false}
        style={{ marginTop: 24, borderTop: '1px solid #efefef' }}>
        {relateImages &&
          relateImages.map((item: any) => {
            return (
              <div
                key={item.id}
                style={{
                  background: '#fff',
                  marginBottom: 12,
                  marginRight: 12,
                  display: 'inline-block',
                  position: 'relative',
                  border: '1px solid #efefef',
                }}>
                <img src={item.url} width="100" height="100" />
                <Checkbox
                  style={{ position: 'absolute', top: -5, right: 0 }}
                  checked={relatedImageId === item.id}
                  onChange={this.onSelectImageChange.bind(this, item)}
                />
              </div>
            );
          })}
      </Card>
    );
  }

  // 音频识别操作
  renderAudioReg() {
    const { recognitionResult } = this.state;
    const { loadingGroup } = this.props;

    return (
      <Card
        className="word-recognition"
        bordered={false}
        title={
          <>
            <Icon type="play-circle-o" />
            &nbsp;音频时间轴识别&nbsp;&nbsp;
            <Button
              loading={isLoading(loadingGroup, ACTIONS.RECOGNITION_AUDIO)}
              type="primary"
              onClick={this.handleAudioRecognition}>
              开始识别
            </Button>
          </>
        }>
        <div className="tip">提示1：需要英文高亮的情况下，才应该使用此语音识别功能</div>
        {recognitionResult && (
          <div style={{ marginTop: 12 }}>
            <div className="result">
              <Tooltip title="音频识别结果可信度">
                <Progress
                  className="result-confidence"
                  type="circle"
                  width={70}
                  format={percent => `${percent}%`}
                  percent={get(recognitionResult, 'confidence', 0).toFixed(2) * 100}
                />
              </Tooltip>
              <div className="result-sentence">
                识别脚本: {get(recognitionResult, 'transcript', '')}
              </div>
            </div>
            <div className="recognition-wrap">
              {recognitionResult.timestamps.map((item: any, index: number) => {
                const [word, start, end] = item;
                return (
                  <div className="word-item" key={index}>
                    <div className="time">{start}</div>
                    <div className="word">{word}</div>
                    <div className="time">{end}</div>
                  </div>
                );
              })}
              <div className="clear" />
            </div>
          </div>
        )}
      </Card>
    );
  }

  renderWordEdit = (word: string, start: any, end: any, index: number, innerIndex: number) => {
    return (
      <Tooltip key={`${index}-${innerIndex}`}>
        <div className="word-inner">
          <ContentEditable
            tip="begin"
            onChange={(value: any) => {
              this.handleWordTimeEdit(value, index, innerIndex, 'start');
            }}>
            {start}
          </ContentEditable>
          <div className="word-edit">
            <span>{word}</span>
          </div>
          <ContentEditable
            tip="end"
            onChange={(value: any) => {
              this.handleWordTimeEdit(value, index, innerIndex, 'end');
            }}>
            {end}
          </ContentEditable>
        </div>
      </Tooltip>
    );
  };

  renderWordList() {
    const { words, editKnowledgeWordIndex } = this.state;
    return words.map((item: any, index: number) => {
      const wordArr = item.name.split(/\s+/);
      const startArr = item.start ? String(item.start).split(',') : [];
      const endArr = item.end ? String(item.end).split(',') : [];
      let iconStyle: any = {
        marginLeft: '-6px',
      };
      let iconTip = '关联知识点或辅助词';
      if (item.knowledgeId || item.assistId) {
        iconStyle = {
          ...iconStyle,
          color: '#1890ff',
        };
      }
      if (item.knowledgeId) {
        iconTip = '已关联知识点';
      } else if (item.assistId) {
        iconTip = '已关联辅助词';
      }

      return (
        <div
          key={index}
          style={{
            display: 'flex',
            alignItems: 'center',
            float: 'left',
            position: 'relative',
            marginRight: 12,
            marginBottom: 12,
            fontSize: '120%',
          }}>
          <div className={`word-wrap ${wordArr.length > 1 ? 'group' : ''}`}>
            {wordArr.map((word: any, innerIndex: number) => {
              return this.renderWordEdit(
                word,
                startArr.length > 0 ? startArr[innerIndex] : '',
                endArr.length > 0 ? endArr[innerIndex] : '',
                index,
                innerIndex
              );
            })}
          </div>
          <Tooltip title={iconTip}>
            <Icon
              style={iconStyle}
              type="book"
              onClick={e => {
                e.stopPropagation();
                this.handleEditKnowledge(index);
              }}
            />
          </Tooltip>
          <div className="clear-both" />
          {editKnowledgeWordIndex === index && (
            <LoadableKnowledgeList
              word={item}
              onVisibleChange={() => {
                this.handleEditKnowledge(-1);
              }}
              onOk={this.handleKnowledgeCallback}
              onCancel={() => {
                this.handleEditKnowledge(-1);
              }}
            />
          )}
        </div>
      );
    });
  }

  renderWords() {
    const {
      words,
      audio,
      sentence,
      editKnowledgeWordIndex,
      isPlaying,
      playingTimeAt,
      audioDuration,
    } = this.state;

    const canHighlight = reduce(
      words,
      (result: boolean, item: any) => {
        const start = get(item, 'start', null);
        const end = get(item, 'end', null);
        return result && start !== null && end !== null;
      },
      words.length > 0
    );

    return (
      <>
        {audio && sentence && this.renderAudioReg()}
        <Card
          bordered={false}
          title={
            <div className="sentence-edit">
              <Icon type="edit" />
              &nbsp;编辑脚本&nbsp;&nbsp;
              {Boolean(canHighlight) && (
                <div className="preview">
                  <div>| 高亮预览：</div>
                  <Progress
                    className="result-confidence"
                    type="circle"
                    width={40}
                    format={(percent: number) => ''}
                    percent={(playingTimeAt / audioDuration) * 100}
                  />
                  {isPlaying ? (
                    <Icon className="play-icon" type="pause" />
                  ) : (
                    <Icon className="play-icon" type="right" onClick={this.handlePlayBegin} />
                  )}
                </div>
              )}
            </div>
          }
          style={{ marginTop: 24, borderTop: '1px solid #efefef' }}>
          <div className="tip">
            提示：在此可以根据预览高亮效果来微调每个单词的时间轴（不需要高亮的脚本请忽略）
          </div>
          {isPlaying ? (
            <LoadableHighlightSentence sentence={words} playingTimeAt={playingTimeAt} />
          ) : (
            this.renderWordList()
          )}
        </Card>
      </>
    );
  }

  render() {
    const { needRelateImages, needEditWords } = this.props;
    return (
      <CustomModal
        width={'80%'}
        style={{ maxWidth: 1244, minWidth: 780 }}
        footer={this.renderFooterActions()}>
        {this.renderInput()}
        {needRelateImages && this.renderImages()}
        {needEditWords && this.renderWords()}
      </CustomModal>
    );
  }
}

interface OwnProps {
  editSentence: any;
  onOk: (data: any) => any;
  onCancel: () => any;
}

function mapStateToProps(state: any, ownProps: OwnProps) {
  const { editSentence, onOk, onCancel } = ownProps;
  const { loadingGroup } = state;
  return {
    onOk,
    onCancel,
    editSentence,
    loadingGroup,
  };
}

export default connect(mapStateToProps)(Form.create()(EditSentence));
